package eval

import (
	"showen/lex"
	"showen/obj"
	"showen/parse"
	"testing"
)

func TestQuasiquoteIntegration(t *testing.T) {
	tests := []struct {
		input string
		want  []string
	}{
		{
			"`less",
			[]string{"less"},
		},
		{
			"`abc 6 5",
			[]string{"abc", "6", "5"},
		},
		{
			"(quasiquote abc) 6 5",
			[]string{"abc", "6", "5"},
		},
		{
			"(quasiquote (1 2 3))",
			[]string{"(1 2 3)"},
		},
		{
			"(define x 'greater)",
			[]string{""},
		},
		{
			"(define x 5) `(list ,x ,(+ x 1))",
			[]string{"", "(list 5 6)"},
		},
		{
			"(define y '(a b)) `(1 ,@y 2)",
			[]string{"", "(1 a b 2)"},
		},
	}

	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := parse.NewParser(tokens)
		exprs, err := parser.ParseProgram()
		if err != nil {
			t.Errorf("ParseProgram() error = %v", err)
			continue
		}
		for i, expr := range exprs {
			obj, err := EvalExpression(expr, env)
			if err != nil {
				t.Errorf("Eval() error = %v", err)
				continue
			}
			if obj.Inspect() != tt.want[i] {
				t.Errorf("Eval() = %v, want %v", obj.Inspect(), tt.want[i])
			}
		}
	}
}

func TestQuasiquoteExamples(t *testing.T) {
	tests := []struct {
		input string
		want  string
	}{
		{"`(list ,(+ 1 2) 4)", "(list 3 4)"},
		{"(define x 5) `(a ,x ,(* x 2))", "(a 5 10)"},
		{"(define lst '(1 2 3)) `(a ,@lst b)", "(a 1 2 3 b)"},
		{"(define lst1 '(1 2)) (define lst2 '(3 4)) `(,@lst1 ,@lst2)", "(1 2 3 4)"},
	}

	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := parse.NewParser(tokens)
		exprs, err := parser.ParseProgram()
		if err != nil {
			t.Fatalf("ParseProgram() error = %v", err)
		}

		var result obj.Object
		for _, expr := range exprs {
			result, err = EvalExpression(expr, env)
			if err != nil {
				t.Fatalf("Eval() error = %v", err)
			}
		}

		if result.Inspect() != tt.want {
			t.Errorf("Eval() = %v, want %v", result.Inspect(), tt.want)
		}
	}
}